﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XCommunity.Data;
using System.Data;
using XCommunity.Documents;
using System.Xml;
using System.IO;
using System.Xml.Linq;
using System.Reflection;

namespace XCommunity.Frames
{
  public class TopicPersister
  {



    public TopicPersister()
    {
    }


    private TopicFrameDataProvider _dataProvider = new TopicFrameDataProvider();

    /// <summary>
    /// 获取数据库访问适配器
    /// </summary>
    public TopicFrameDataProvider DataProvider
    {
      get { return _dataProvider; }
    }


    /// <summary>
    /// 加载一个话题的元数据
    /// </summary>
    /// <param name="id">话题ID</param>
    /// <returns></returns>
    public virtual TopicMeta LoadTopicMeta( int id )
    {

      DataRow dataItem = TopicFrameDataProvider.DbUtility.ExecuteSingleRow( "SELECT ID, Guid, Title, Launcher, LaunchDate FROM Topics WHERE ID = {0}", id );

      if ( dataItem == null )
        throw new DataNotFoundException( typeof( TopicMeta ), id );

      TopicMeta meta = new TopicMeta();

      meta.TopicId = id;
      meta.Guid = (Guid) dataItem["Guid"];
      meta.Saved = true;

      meta.Title = (string) dataItem["Title"];
      meta.Launcher = (string) dataItem["Launcher"];
      meta.LaunchDate = DateTime.SpecifyKind( (DateTime) dataItem["LaunchDate"], DateTimeKind.Utc );

      return meta;
    }


    /// <summary>
    /// 加载一个话题框架
    /// </summary>
    /// <param name="service">框架服务</param>
    /// <param name="TopicMeta">话题元数据</param>
    /// <returns></returns>
    public virtual ITopicFrame LoadTopicFrame( FrameService service, TopicMeta meta )
    {

      var dataItem = TopicFrameDataProvider.DbUtility.ExecuteSingleRow( "SELECT TopicFrameType, CommunitySystem, Guid FROM Topics WHERE ID = {0};", meta.TopicId );

      string topicFrameType = dataItem.Field<string>( "TopicFrameType" );
      Guid communitySystemId = dataItem.Field<Guid>( "CommunitySystem" );


      var type = Type.GetType( topicFrameType );

      ITopicFrame topic = (ITopicFrame) CreateTopicFrameInstance( type, service, meta, communitySystemId );
      IPersistableTopicFrame persistableTopic = topic as IPersistableTopicFrame;

      if ( persistableTopic == null )
        return topic;



      string filepath = GetTopicFrameDataFilepath( persistableTopic );

      using ( Stream stream = new FileStream( filepath, FileMode.Open, FileAccess.Read ) )
      {
        persistableTopic.Load( stream );
      }

      return persistableTopic;
    }



    /// <summary>
    /// 在加载话题框架时，创建话题框架实例。
    /// </summary>
    /// <param name="type">话题框架对象类型</param>
    /// <param name="service">管理此对象的框架服务</param>
    /// <param name="topicId">话题编号</param>
    /// <param name="communitySystemId">所属社区系统的标识</param>
    /// <returns></returns>
    protected virtual ITopicFrame CreateTopicFrameInstance( Type type, FrameService service, TopicMeta meta, Guid communitySystemId )
    {
      ConstructorInfo constructor = type.GetConstructor( new Type[] { typeof( FrameService ), typeof( TopicMeta ), typeof( ICommunitySystem ) } );

      return (ITopicFrame) constructor.Invoke( new object[] { service, meta, service.Context.GetCommunitySystem( communitySystemId ) } );
    }



    /// <summary>
    /// 保存一个新增的话题元数据
    /// </summary>
    /// <param name="meta">话题元数据</param>
    /// <returns>话题ID</returns>
    public virtual int SaveTopic( TopicMeta meta )
    {
      meta.LaunchDate = DateTime.UtcNow;
      meta.Guid = Guid.NewGuid();

      int topicId = Convert.ToInt32( TopicFrameDataProvider.DbUtility.ExecuteScalar( "INSERT INTO Topics ( Guid, Title, Launcher, LaunchDate, TopicFrameType ) VALUES ( {...} ); SELECT @@Identity;",
                                                                                            meta.Guid, meta.Title, meta.Launcher, meta.LaunchDate, "" ) );

      meta.TopicId = topicId;
      meta.Saved = true;

      return topicId;
    }



    /// <summary>
    /// 保存一个新增的话题框架
    /// </summary>
    /// <param name="topic">话题框架</param>
    public virtual void SaveTopic( ITopicFrame topic )
    {

      if ( !string.IsNullOrEmpty( (string) TopicFrameDataProvider.DbUtility.ExecuteScalar( "SELECT TopicFrameType FROM Topics WHERE ID = {0}", topic.TopicId ) ) )
        throw new InvalidOperationException();//UNDONE 强类型异常。如果话题已被保存过。

      TopicFrameDataProvider.DbUtility.ExecuteNonQuery( "UPDATE Topics SET TopicFrameType = {1}, CommunitySystem = {2} WHERE ID = {0}", topic.TopicId, topic.GetType().AssemblyQualifiedName, topic.CommunitySystem.Guid );

      IPersistableTopicFrame persistableTopic = topic as IPersistableTopicFrame;

      if ( persistableTopic == null )
        return;


      string filepath = GetTopicFrameDataFilepath( persistableTopic );

      using ( Stream stream = new FileStream( filepath, FileMode.Create, FileAccess.Write ) )
      {
        persistableTopic.Save( stream );
      }
    }



    /// <summary>
    /// 此方法返回保存话题框架数据的文件名
    /// </summary>
    /// <param name="topic"></param>
    /// <returns></returns>
    protected virtual string GetTopicFrameDataFilepath( IPersistableTopicFrame topic )
    {
      return Path.Combine( @"C:\XCommunity\Topics", string.Format( "{0}.topic", topic.TopicId ) );
    }



    /// <summary>
    /// 将一个文档发布到话题
    /// </summary>
    /// <param name="topicId">发布到的话题</param>
    /// <param name="document">被发布的文档</param>
    /// <param name="method">发布的方法或用途</param>
    public virtual void PublishDocument( int topicId, DocumentMeta document, string method )
    {
      DataProvider.PublishDocument( topicId, document.DocumentId, method );
    }


    /// <summary>
    /// 获取一个流，用于加载或更新话题框架数据
    /// </summary>
    /// <param name="topic">话题框架</param>
    /// <returns></returns>
    public virtual Stream GetFrameDataStream( IPersistableTopicFrame topic )
    {
      string filepath = GetTopicFrameDataFilepath( topic );

      return new FileStream( filepath, FileMode.OpenOrCreate, FileAccess.ReadWrite );
    }


  }
}
